#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "pthread.h"

#include "project.h"
#include "binarybuffer.h"
#include "util.h"
#include "node_backup.h"
#include "getMemory.h"

static void restore_tree(char*);
static int analyse_backup_cell(char*);
void backup_insert_toTree(char*);
static void empty_hash_table();

/*
 * 初始化配置文件
 * 启动时将备份的缓存节点还原
 * 启动备份线程，节点失效线程
 * 还有一些初始化工作
 */
void
init_tree ()
{
    init_profile();
    empty_hash_table();

    ALL_BUFFER_SIZE = 0;
    
    char *back_file = concat_path_name(PROFILEPATH, BACKFILE);
    if (access(back_file, F_OK) != 0)
    {
        /* 备份文件不存在，则建立备份文件，不退出程序 */
        fprintf(stderr, "Back file not exist!\n");
        char *backfile = (char *)malloc(6 + strlen(back_file) + 1);
        sprintf(backfile, "touch %s", back_file);
        system(backfile);
        free(backfile);
        free(back_file);
        return ;
    }
    
    /* 备份还原 */
    restore_tree(back_file);
    free(back_file);
    
    /* 创建备份线程 */
    pthread_t backthread;
    int ret = pthread_create(&backthread, NULL, backup_tree, NULL);
    if (ret != 0)
    {
        fprintf(stderr, "create backup thread error\n");
        exit(-1);
    }
    ret = pthread_detach(backthread);
    if (ret != 0)
    {
        fprintf(stderr, "set backup thread detach error\n");
        exit(-1);
    }
    /* 创建备份线程结束 */
}

/*
 * 缓存节点还原
 * one section just like [fl_name,fl_count,fl_size]
 */
static void
restore_tree (char *back_file)
{
    FILE *backup_fd;
    int len;
    char *backup_str = (char *)malloc(SECTION_LENGTH);
    
    len = 0;
    backup_fd = fopen(back_file, "r");
    while (fread(backup_str + len, 1, SECTION_LENGTH, backup_fd) > 0)
    	len = analyse_backup_cell(backup_str);

    free(backup_str);
    fclose(backup_fd);
}

/*
 * 分析读到的备份句子，获取文件节点[]
 * @return 分析完的长度
 */
static int
analyse_backup_cell (char *backup_str)
{
    int pos = 0;
    char *fileCell = (char *)malloc(SECTION_LENGTH);
    char *tmp = strdup(backup_str);
    memset(fileCell, 0, SECTION_LENGTH);
    
    while (*(++tmp))
    {
        if (*tmp != ']')
            *(fileCell + pos++) = *tmp;
        else
        {
        	backup_insert_toTree(fileCell);         //insert
        	memset(fileCell, 0, SECTION_LENGTH);
        	pos = 0;
        	tmp++;
        }
    }
    free(fileCell);

    int len1, len2, i, diff_len;
    len1 = strlen(backup_str);
    len2 = strlen(strrchr(backup_str, '['));
    diff_len = len1 - len2;

    for (i = 0; i < len2; i++)
        *(backup_str + i) = *(backup_str + i + diff_len);
    *(backup_str + len2) = '\0';

    return len2;
}

/*
 * 将备份中的文件节点插入到树中
 */
void
backup_insert_toTree (char *cell)
{
    /* first should analyse cell to get name,size,count */
    /* format of cell: name,count,size */
    char *name = (char *)malloc(FILENAME_LENGTH);
    int count = 0;
    long size = 0;
    int pos = 0;
    while (*cell != ',')
    {
        *(name + pos++) = *cell++;
    }
    *(name + pos) = '\0';
    cell++;
    while (*(cell) != ',')
    {
        count = count * 10 + (*cell++) - '0';
    }
    cell++;
    while (*cell)
    {
        size = size * 10 + (*cell++) - '0';
    }
   
    F_FILE ff = checkIfCache(size, count, name);
    if (ff)
    {
        int i;
        F_BUF prev = NULL, ne;
        char *full_name = concat_path_name(FILEPATH, name);
        FILE *fp = fopen(full_name, "r");
        for (;;)
        {
            ne = new(struct f_buf);
            ne->block_buf = (char *) malloc (BLOCK_SIZE + 1);
            ne->next = NULL;
            
            i = atom_read(fp, ne->block_buf, BLOCK_SIZE);
            if (i <= 0)
                break;
            ne->block_buf[i] = '\0';
            
            if (!prev)
            {
                ff->fl_buf = ne;
                prev = ne;
            }
            else
            {
                prev->next = ne;
                prev = ne;
            }
        }
        
        ne = new(struct f_buf);
        ne->block_buf = NULL;
        ne->next = NULL;
        prev->next = ne;
        
        free(full_name);
        fclose(fp);
    }
    free(name);
}

static void
empty_hash_table ()
{
    f_hash_arr = (F_HASH) malloc(HASH_TABLE_SIZE * sizeof(struct f_hash));
    int i;
    for (i = HASH_TABLE_SIZE - 1; i >= 0; i--)
    {
        f_hash_arr[i].ha_file_count = 0;
        f_hash_arr[i].ha_size = 0;
        f_hash_arr[i].ha_file = NULL;
    }
}

char *
concat_path_name (const char *path, const char *f_name)
{
    int len = strlen(path) + strlen(f_name) + 2;
	char *full_name = (char*)malloc(len);
	/*第一次分配内存失败，等待2秒再申请一次*/
	if (full_name == NULL)
	{
		sleep(2);
		full_name = (char*)malloc(len);
		if (full_name == NULL)
		{
			fprintf(stderr, "Error: alloc Memory error");
			exit(3);
		}
	}
    
    if (*(path + strlen(path)) == '/')
        sprintf(full_name, "%s%s", path, f_name);
    else
        sprintf(full_name, "%s/%s", path, f_name);

	return full_name;
}

/*
 * 插入二叉树时内部调用，只获取文件大小，和外部调用get_file_size()有区别。此函数不需要开线程
 */
long
get_inner_file_size (const char *f_name)
{
	struct stat st;
	char *full_name = concat_path_name(FILEPATH, f_name);
	stat(full_name, &st);

	return st.st_size;
}